(圖片來源:twitter @oleg_kovalov)
在開發時遲早會遇上型別不一致的時候,而Go採用嚴格的型別系統(Go沒有隱性型別轉換implicit type conversion),此時我們有兩個選擇 :
<型別>(<值>)
例如 :
從rune轉為string。
string([]rune)
從string轉為rune。
[]rune(string)
字串轉換之所以可行,是因為它是使用bytes切片來儲存資料。
因此,字串轉換不會損失。但是,其他型別轉換就不見得惹。
像是將 int64 轉 int8,或是uint轉成int64,由於儲存數字變小了,就有可能發生溢位!!!!
又或是把int轉成float也有可能溢位。
因為浮點數會把儲存空間分割成整數和小數位,至於將float轉成int,小數直接被截去。
** Note : 也有些Go核心型別之間無法轉換的,例如字串不能直接轉換成數字、布林值不能直接轉成字串或數字。**
不過 strconv 套件提供了一系列能將字串轉為可指定型別的功能
package main
import (
"fmt"
)
func main() {
int64Value := int64(300)
int8Value := int8(int64Value)
fmt.Println(int8Value)
}
在這個範例中,將一個int64值(300)轉換為int8,但int8只能表示-128到127之間的整數,因此溢位將會發生,結果將會是-56。
到目前為止,使用了許多fmt.Println()來印出資料,然而為何這樣一個函式可以接收任何型別值做為參數呢?
我們可以看一下Go標準函式庫定義 :
func Print(a...interface{}) (n int, err error) {
return Fprint(os.Stout, a...)
}
Print 函式接受可變數量的參數 a,這些參數的型別為 interface{},也就是可以接受任意型別的參數。
(n int, err error) 是函式的回傳值列表,其中 n 是輸出的位元組數,err 是可能的錯誤。
函式內部使用了 Fprint 函式來執行實際的輸出運算,但輸出的目標是標準輸出 os.Stdout。
Fprint 函式的第一個參數是輸出目標,第二個參數是要輸出的內容,它使用 ... 語法表示可以接受可變數量的參數。
介面會在後續篇幅做為介紹,可以先知道介面的定義就是:
介面型別是一種規範,會列出若干函式的定義,而任何型別只要具備相同的函式,就會被視為符合該介面型別。
介面不會決定傳入值得變成什麼型別,它只會決定哪些型別符合資格
介面是一種抽象類型,可以容納不同底層類型的值。
類型斷言是在運行時檢查介面值的實際類型,並將其轉換為該類型。 這可以讓程式設計師安全地存取介面值的底層類型的屬性和方法。
在某些語言中,類型斷言有兩種形式:型別斷言和型別斷言表達式。
<值> := <變數名稱>.(<型別>)
此時你還可以接收第二個選擇性回傳值,代表轉成功與否 :
<值>, <ok> := <變數名稱>.(<型別>)
如果不接收第二回傳值的話,而斷言失敗,Go語言會引發panic
下面是一個使用Go語言中的類型斷言的範例:
func printLength(x interface{}) {
if str, ok := x.(string); ok {
fmt.Printf("Length of string: %d\n", len(str))
} else if arr, ok := x.([]int); ok {
fmt.Printf("Length of slice: %d\n", len(arr))
} else {
fmt.Println("Unknown type")
}
}
func main() {
s := "Hello, World!"
a := []int{1, 2, 3, 4, 5}
printLength(s) // Length of string: 13
printLength(a) // Length of slice: 5
}
空介面的用處 ?
答案是沒有用處,但它仍然是個可以傳給函式的值,
那些型別符合空介面?
答案是~所有型別,必然符合空介面的型別。
只要結合interface{} 和型別斷言,就可以幫你繞過Go語言嚴格型別控制,讓你建立可接受任何型別做為參數的函式。缺點就是會失去Go提供的安全型別保護,此時確保型別安全的的責任,就是你扛啦。
前幾篇有提到switch的部分,當我們將switch結合型別斷言,它就叫做type switch 啦!
此時,型別的switch只會執行符合型別的case所對應的程式敘述,並把值設定成那個型別。
我們可以在case比對一種以上的型別,但這樣Go就無法幫你自動調整值型別,我們還是得在case底下加入額外的型別斷言。
package main
import (
"fmt"
)
func main() {
var val interface{}
val = 42
switch val.(type) {
case int:
fmt.Println("val is int")
case string:
fmt.Println("val is string")
default:
fmt.Println("val not int nor string")
}
}
函式範例 :
package main
import (
"fmt"
)
func processValue(val interface{}) {
switch val.(type) {
case int:
fmt.Println("val 是一個整數")
case string:
fmt.Println("val 是一個字串")
default:
fmt.Println("val 不是整數也不是字串")
}
}
func main() {
var val interface{} // 定義一個空介面類型的變數
val = 42 // 將一個整數賦值給空介面
processValue(val)
val = "Hello, World!" // 將一個字串賦值給空介面
processValue(val)
val = 3.14 // 將一個浮點數賦值給空介面
processValue(val)
}
在 Go 語言中,panic 是一種執行時間錯誤,用於表示程式發生了一個嚴重的錯誤,通常是由於不可恢復的錯誤或異常情況導致的。 當程式執行到一個 panic 語句時,它會立即中斷目前的執行流程,並開始執行程式的 panic 處理流程。
以下是關於 panic 的一些重要資訊和用法:
**觸發 Panic: **可以使用 panic 函數明確觸發 panic,也可以在某些情況下由執行時自動觸發。 例如,陣列越界、除以零等情況都會導致自動觸發 panic。
**Panic 處理: **當程式觸發 panic 時,它會立即停止目前的執行流程,然後開始執行 panic 處理流程。 這包括運行程式中的 deferred 函數(透過 defer 聲明的函數)和可能的 recover 操作。
**Recover: **recover 是用於捕獲 panic 並進行處理的內建函數。 它只能在 deferred 函數內部使用。 recover 的作用是終止 panic 過程,並傳回 panic 值(如果有的話)。 如果沒有 panic,recover 將傳回 nil。 透過 recover,可以在程式發生 panic 時執行一些清理工作或進行錯誤處理,從而避免程式崩潰。
以下是一個範例,演示了 panic 和 recover 的使用:
package main
import (
"fmt"
)
func main() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered from panic:", r)
}
}()
fmt.Println("Start of the program")
// 觸發 panic
panic("A serious error occurred")
fmt.Println("End of the program") // 這行程式碼不會執行
}
在這個範例中,我們使用 defer 來延遲執行一個函數,該函數包含了 recover。 當程式觸發 panic 時,recover 將捕獲 panic,列印錯誤訊息,並使程式繼續執行,而不會崩潰。
需要注意的是,通常情況下,不建議濫用 panic 和 recover,應該盡量避免觸發 panic,而是透過錯誤處理來處理各種異常情況。 只有在特定情況下,如不可恢復的錯誤,或在庫中需要執行清理操作時,才應該使用 panic 和 recover。
以上就是今天的內容,接下來的主題將會進入完整滿滿的函式的介紹~~~~